#ifndef __CLINET_SIZE_H__
#define __CLIENT_SIZE_H__

#include <Windows.h>
#include <winsock.h>
#include "common.h"

/*
 * this function verify whois response
 */ 
BOOL verifyServerHelloMsg(size_t suid,char* pBuffer,unsigned short* pTcpPortNumber)
{
	
   unsigned short protocolPrefix = ntohs(MEM_TO_VAL(pBuffer,unsigned short));
   
   char msgId = MEM_TO_VAL(pBuffer+2,char);
   size_t incommingServerUid = ntohl(MEM_TO_VAL(pBuffer+3,size_t));
   unsigned short portNumber = ntohs(MEM_TO_VAL(pBuffer+7,unsigned short));
   size_t controllNumber = ntohl(MEM_TO_VAL(pBuffer+9,size_t));
   

   //1. check msg prefix equals 0xF00D
   //2. cehck msg id == 0x02
   //3. verify ans's server uid eauals reauest server uid
   //4. calculate XOR(serverUID,0xFEEDBEBE) - 4*XOR(portNumber,0x0980) and check if this equal the controll number 

   if (	protocolPrefix != PROTOCOL_PREFIX || msgId != SERVER_HELLO_MSG_ID || incommingServerUid != suid ||
		((incommingServerUid^SERVER_UID_KEY) - (4*(portNumber^SERVER_TCP_PORT_KEY))) != controllNumber)
   {
      printf("Error: unable to verify server identity\n");
	  return FALSE;
   }
   
   *pTcpPortNumber = portNumber;
   return TRUE;
   
}

/*
 * this method will resolve the server ip via it's uid
 */
BOOL sendClientHelloMsg(const int suid,unsigned long* pIp,unsigned short* pPort)
{
	WSADATA w;								/* Used to open Windows connection */
	SOCKET sd;								/* The socket descriptor */
	struct sockaddr_in server;				/* Information about the server */
	int server_length=sizeof(server);						/* Length of server struct */
	BOOL retValue = FALSE;
	//this buffer contain the server uid to ip resolve request
	char reqBuffer[CLIENT_HELLO_MSG_LEN];
	char ansBuffer[SERVER_HELLO_MSG_LEN];
	fd_set readfds;

	struct timeval timeout;
	int selectRes;

	//wait 5 seconds for server response
	timeout.tv_sec = TIMEOUT_SECONDS;
	timeout.tv_usec = 0;
	
	
	MEM_TO_VAL(reqBuffer,size_t) = htons(PROTOCOL_PREFIX);
	MEM_TO_VAL(reqBuffer+2,char) = CLIENT_HELLO_MSG_ID;
	MEM_TO_VAL(reqBuffer+3,size_t) = htonl(suid);

	

	//Open windows connection
	if (WSAStartup(0x0101, &w) != 0)
	{
		printf("Error: Could not open Windows connection.\n");
		return FALSE;
	}
	
	do
	{
	   sd = createUDPSocket(CLIENT_UDP_PORT,TRUE);
	   
	   if (sd == INVALID_SOCKET)
	   {
		   printf("Error: Could not create socket.\n");
		   break;
	   }
	
	   //Clear out server struct
	   memset((void *)&server, '\0', sizeof(struct sockaddr_in));

	   //Set family and port
	   server.sin_family = AF_INET;
	   server.sin_port = htons(SERVER_UDP_PORT);

	   //Set server address - brodcast
	   server.sin_addr.S_un.S_addr = 0xFFFFFFFF;
	
	   
		
	   //send the brodcast request
	   
	   if (sendto(sd,(char*)&reqBuffer, CLIENT_HELLO_MSG_LEN,0,(sockaddr*)(&server),sizeof(server)) != CLIENT_HELLO_MSG_LEN)
	   {
	      printf("Error: can't sent whole ip resolve brodcast\n");
	      break;
	   }
	  
	   FD_ZERO(&readfds);

#pragma warning(suppress:4127)
	   FD_SET(sd,&readfds);
	   
	   selectRes = select(sd+1,&readfds,NULL,NULL,&timeout);
	   
	   if (selectRes == 0 || FD_ISSET(sd,&readfds)==0)
	   {
	      printf("Error: no answer for ip resolve request\n");
	      break;
	   }
	   
	   if (recvfrom(sd, (char*)&ansBuffer, SERVER_HELLO_MSG_LEN, 0, (sockaddr *)&server, &server_length) != SERVER_HELLO_MSG_LEN)
	   {
		  printf("Error: can't receive server's response: %d.\n",WSAGetLastError());
	      break;
	   }
       if(!verifyServerHelloMsg(suid,(char*)&ansBuffer,pPort))
	   {

  		   printf("Error: server is not authenticated\n");
		   break;
	   }
	   *pIp = server.sin_addr.S_un.S_addr;
	   retValue = TRUE;

#pragma warning(suppress:4127)
	}while(FALSE);
	   
	   
	if (sd != INVALID_SOCKET )
	{
	   closesocket(sd);
	}

	WSACleanup();
	
	return retValue;;

}

/*
 * this function sends file content request via TCP connection
 */
BOOL sendFileRequest(SOCKET sd,char* pFilePath,unsigned short filePathLength)
{
	//add one for the \0 that sprintf_s will enter
	size_t reqBufferSize = (filePathLength+FILE_REQ_HEADER_SIZE+1)*sizeof(char);
	char* pReqBuffer = NULL;
	BOOL retVal = FALSE;

	pReqBuffer = (char*)malloc(reqBufferSize);

	if (pReqBuffer == NULL)
	{
		printf("Error: malloc failed\n");
		return FALSE;
	}

	do
	{
		//set header's values
		MEM_TO_VAL(pReqBuffer,unsigned short) = htons(PROTOCOL_PREFIX);
		MEM_TO_VAL(pReqBuffer+2,char) = FILE_CONTENT_REQ_MSG_ID;
		MEM_TO_VAL(pReqBuffer+3,unsigned short) = htons(filePathLength);
		//copy path 
		strncpy_s(pReqBuffer+5,(filePathLength+1)*sizeof(char),pFilePath,_TRUNCATE);

		if (sendTcpBuffer(sd,pReqBuffer,reqBufferSize-1) != (int)(reqBufferSize-1))
	    {
		    printf("Error: can't send file request to the server\n");
		    break;
	    }

		retVal = TRUE;

#pragma warning(suppress:4127) //enable single exit point
	}while(FALSE);

	if (pReqBuffer != NULL)
	{
		free(pReqBuffer);
	}

	return retVal;

}

/*
 * this function checks that incomming file header is match the protocol requirements
 */
BOOL verifyIncommingFileHeader(char* pBuffer,size_t* fileSize)
{
	unsigned short  protocolPrefix = ntohs(MEM_TO_VAL(pBuffer,unsigned short));
   
    char msgId = MEM_TO_VAL(pBuffer+2,char);

    *fileSize = ntohl(MEM_TO_VAL(pBuffer+3,size_t));

	return (protocolPrefix == PROTOCOL_PREFIX && msgId == FILE_CONTENT_RES_MSG_ID);
}


/*
 * this function extract the file substring from a given path by searching the last '\' char
 */
size_t findFileNameFromPath(char* filePath,size_t filePathLength)
{
	while(filePathLength > 0 && filePath[filePathLength-1] != '\\')
	{
		filePathLength--;
	}

	return filePathLength;
}

/*
 * this function establish TCP connection
 */
SOCKET connectToServer(unsigned long ip,unsigned short port)
{
	struct sockaddr_in server;				/* Information about the server */
	SOCKET sd;

	do
	{
			   // Open a datagram socket
	   sd = socket(AF_INET, SOCK_STREAM, 0);
	
	   if (sd == INVALID_SOCKET)
	   {
		   printf("Error: Could not create socket.\n");
		   break;
	   }
		
	   //Clear out server struct
	   memset((void *)&server, '\0', sizeof(struct sockaddr_in));

	   //Set family and port
	   server.sin_family = AF_INET;
	   server.sin_port = htons(port);

	   //Set server address
	   server.sin_addr.S_un.S_addr = ip;

	   
	   if (connect(sd,(sockaddr*)(&server),sizeof(server)))
	   {
		   printf("Error: can't establish TCP connection to the server\n");
		   closesocket(sd);
		   break;
	   }
	   return sd;
#pragma warning(suppress:4127) //enable one single exit point
	}while(FALSE);

	return INVALID_SOCKET;
}




#endif
